<!--
 * @Author: 秦少卫
 * @Date: 2023-04-06 23:04:38
 * @LastEditors: 秦少卫
 * @LastEditTime: 2024-10-07 17:19:51
 * @Description: 图片滤镜
-->

<template>
  <div v-if="isOne && state.type === 'image'" class="box">
    <Divider plain orientation="left">
      <h4>图片滤镜</h4>
    </Divider>
    <Collapse>
      <Panel name="1">
        {{ $t('filters.simple') }}
        <template #content>
          <div class="filter-box">
            <!-- 无参数滤镜 -->
            <div class="filter-item" v-for="(value, key) in state.noParamsFilters" :key="key">
              <img
                :src="getImageUrl(key)"
                alt=""
                @click="changeFilters(key, !noParamsFilters[key])"
              />
              <Checkbox
                v-model="state.noParamsFilters[key]"
                @on-change="(val) => changeFilters(key, val)"
              >
                {{ $t('filters.' + key) }}
              </Checkbox>
            </div>
          </div>
        </template>
      </Panel>
      <Panel name="2">
        {{ $t('filters.complex') }}
        <template #content>
          <!-- 有参数滤镜与组合参数滤镜 -->
          <div>
            <div
              class="filter-item has-params"
              v-for="item in [...state.paramsFilters, ...state.combinationFilters]"
              :key="item.type"
            >
              <Checkbox v-model="item.status" @on-change="changeFiltersByParams(item.type)">
                {{ $t('filters.' + item.type) }}
              </Checkbox>
              <div v-if="item.status" class="content">
                <div class="content slider-box" v-for="info in item.params" :key="info">
                  <div v-if="info.uiType === uiType.SELECT">
                    <RadioGroup v-model="info.value" @on-change="changeFiltersByParams(item.type)">
                      <Radio :label="listItem" v-for="listItem in info.list" :key="listItem">
                        {{ $t('filters.' + item.type + 'List.' + listItem) }}
                      </Radio>
                    </RadioGroup>
                  </div>
                  <div v-if="info.uiType === uiType.NUMBER">
                    <Slider
                      v-model="info.value"
                      :max="info.max"
                      :min="info.min"
                      :step="info.step"
                      @on-input="changeFiltersByParams(item.type)"
                    ></Slider>
                  </div>
                  <div v-if="info.uiType === uiType.COLOR">
                    <ColorPicker
                      v-model="info.value"
                      alpha
                      size="small"
                      @on-change="changeFiltersByParams(item.type)"
                    />
                  </div>
                </div>
              </div>
            </div>
          </div>
        </template>
      </Panel>
    </Collapse>
  </div>
</template>

<script name="Filter" setup>
import useSelect from '@/hooks/select';
import { uiType, paramsFilters, combinationFilters } from '@/config/constants/filter';

const { fabric, isOne, canvasEditor } = useSelect();
const update = getCurrentInstance();
// 无参数滤镜
const noParamsFilters = {
  BlackWhite: false,
  Brownie: false,
  Vintage: false,
  Kodachrome: false,
  technicolor: false,
  Polaroid: false,
  Invert: false,
  Sepia: false,
};

const state = reactive({
  uiType,
  noParamsFilters,
  paramsFilters: [...paramsFilters],
  combinationFilters: [...combinationFilters],
  type: '',
});

// 无参数滤镜修改状态
const changeFilters = (type, value) => {
  const activeObject = canvasEditor.canvas.getActiveObjects()[0];
  state.noParamsFilters[type] = value;
  if (value) {
    const itemFilter = _getFilter(activeObject, type);
    if (!itemFilter) {
      _createFilter(activeObject, type);
    }
  } else {
    _removeFilter(activeObject, type);
  }
};
// 有参数与组合滤镜修改
const changeFiltersByParams = (type) => {
  const activeObject = canvasEditor.canvas.getActiveObjects()[0];
  const filtersAll = [...state.paramsFilters, ...state.combinationFilters];
  const moduleInfo = filtersAll.find((item) => item.type === type);
  if (moduleInfo.status) {
    // 组合参数滤镜修改
    if (moduleInfo.handler) {
      _changeAttrByHandler(moduleInfo);
    } else {
      // 有参数滤镜修改
      moduleInfo.params.forEach((paramsItem) => {
        _changeAttr(type, paramsItem.key, paramsItem.value);
      });
    }
  } else {
    _removeFilter(activeObject, type);
  }
};

const handleSelectOne = () => {
  const activeObject = canvasEditor.canvas.getActiveObjects()[0];
  if (activeObject) {
    state.type = activeObject.type;
    if (state.type === 'image') {
      // 无参数滤镜回显
      Object.keys(noParamsFilters).forEach((type) => {
        state.noParamsFilters[type] = !!_getFilter(activeObject, type);
        update?.proxy?.$forceUpdate();
      });
      // 有参数滤镜回显
      paramsFilters.forEach((filterItem) => {
        const moduleInfo = state.paramsFilters.find((item) => item.type === filterItem.type);
        const filterInfo = _getFilter(activeObject, filterItem.type);
        moduleInfo.status = !!filterInfo;
        moduleInfo.params.forEach((paramsItem) => {
          paramsItem.value = filterInfo ? filterInfo[paramsItem.key] : paramsItem.value;
        });
      });

      // 组合滤镜回显
      combinationFilters.forEach((filterItem) => {
        const moduleInfo = state.combinationFilters.find((item) => item.type === filterItem.type);
        const filterInfo = _getFilter(activeObject, filterItem.type);
        moduleInfo.status = !!filterInfo;
        // 不回显具体参数
      });
    }
    update?.proxy?.$forceUpdate();
  }
};

onMounted(() => {
  canvasEditor.on('selectOne', handleSelectOne);
});

onBeforeUnmount(() => {
  canvasEditor.off('selectOne', handleSelectOne);
});

// 图片地址拼接
function getImageUrl(name) {
  return new URL(`../assets/filters/${name}.png`, import.meta.url).href;
}

// 设置滤镜值
function _changeAttr(type, key, value) {
  const activeObject = canvasEditor.canvas.getActiveObjects()[0];
  const itemFilter = _getFilter(activeObject, type);
  if (itemFilter) {
    itemFilter[key] = value;
  } else {
    const imgFilter = _createFilter(activeObject, type);
    imgFilter[key] = value;
  }
  activeObject.applyFilters();
  canvasEditor.canvas.renderAll();
}

function _changeAttrByHandler(moduleInfo) {
  const activeObject = canvasEditor.canvas.getActiveObjects()[0];
  // 删除
  _removeFilter(activeObject, moduleInfo.type);
  // 创建
  const params = moduleInfo.params.map((item) => item.value);
  _createFilter(activeObject, moduleInfo.type, moduleInfo.handler(...params));
}

/**
 * Create filter instance
 * @param {fabric.Image} sourceImg - Source image to apply filter
 * @param {string} type - Filter type
 * @param {Object} [options] - Options of filter
 * @returns {Object} Fabric object of filter
 * @private
 */
function _createFilter(sourceImg, type, options = null) {
  let filterObj;
  // capitalize first letter for matching with fabric image filter name
  const fabricType = _getFabricFilterType(type);
  const ImageFilter = fabric.Image.filters[fabricType];
  if (ImageFilter) {
    filterObj = new ImageFilter(options);
    filterObj.options = options;
    sourceImg.filters.push(filterObj);
  }
  sourceImg.applyFilters();
  canvasEditor.canvas.renderAll();
  return filterObj;
}
/**
 * Get applied filter instance
 * @param {fabric.Image} sourceImg - Source image to apply filter
 * @param {string} type - Filter type
 * @returns {Object} Fabric object of filter
 * @private
 */
function _getFilter(sourceImg, type) {
  let imgFilter = null;

  if (sourceImg) {
    const fabricType = _getFabricFilterType(type);
    const { length } = sourceImg.filters;
    let item, i;

    for (i = 0; i < length; i += 1) {
      item = sourceImg.filters[i];
      if (item.type === fabricType) {
        imgFilter = item;
        break;
      }
    }
  }

  return imgFilter;
}
/**
 * Remove applied filter instance
 * @param {fabric.Image} sourceImg - Source image to apply filter
 * @param {string} type - Filter type
 * @private
 */
function _removeFilter(sourceImg, type) {
  const fabricType = _getFabricFilterType(type);
  sourceImg.filters = sourceImg.filters.filter((value) => value.type !== fabricType);
  sourceImg.applyFilters();
  canvasEditor.canvas.renderAll();
}
/**
 * Change filter class name to fabric's, especially capitalizing first letter
 * @param {string} type - Filter type
 * @example
 * 'grayscale' -> 'Grayscale'
 * @returns {string} Fabric filter class name
 */
function _getFabricFilterType(type) {
  return type.charAt(0).toUpperCase() + type.slice(1);
}
</script>

<style scoped lang="less">
.filter-box {
  overflow: hidden;
  .filter-item {
    float: left;
    cursor: pointer;
    width: 50%;
    margin-bottom: 10px;
    img {
      width: 90%;
      height: auto;
    }
  }
}
.has-params {
  display: inline-block;
  margin-bottom: 10px;
  width: 50%;
  .content {
    width: 90%;
  }
  cursor: none;
}
.box {
  margin-bottom: 12px;
}
</style>
